/**
 * 决策链组件
 * 
 * 功能描述：
 * 1. 展示3D力导向图（Force Graph 3D）
 * 2. 可视化决策节点及其层级关系
 * 3. 支持节点点击交互，显示详细信息
 * 4. 动态箭头动画效果，展示数据流向
 * 
 * 核心技术：
 * - 3d-force-graph: 用于构建3D力导向图
 * - Three.js: 用于自定义节点渲染（精灵图文本、球体、发光效果）
 */
<template>
  <div class="decision-chain-container">
    <!-- 3D力导向图容器 -->
    <div ref="graphContainer" class="graph-container"></div>

    <!-- 控制面板 -->
    <!-- <div class="control-panel">
      <button class="reset-btn" @click="resetCamera">重置视角</button>
    </div> -->

    <!-- 信息面板 -->
    <div v-if="selectedNode" class="info-panel">
      <div class="info-header">
        <h3>{{ selectedNode.name }}</h3>
        <button class="close-btn" @click="selectedNode = null">×</button>
      </div>
      <div class="info-content">
        <div class="info-section">
          <div class="section-label">分析思路</div>
          <div class="section-content analysis-content">
            <template v-if="selectedNode.analysisThought && selectedNode.analysisThought.length > 0">
              <div v-for="(item, index) in selectedNode.analysisThought" :key="'thought-' + index" class="content-item">
                {{ item }}
              </div>
            </template>
            <div v-else class="content-item">暂无数据</div>
          </div>
        </div>
        
        <div class="info-section">
          <div class="section-label">输入节点</div>
          <div class="section-content input-content">
            <template v-if="selectedNode.inputNodes && selectedNode.inputNodes.length > 0">
              <div v-for="(item, index) in selectedNode.inputNodes" :key="'input-' + index" class="input-item">
                {{ item }}
              </div>
            </template>
            <div v-else class="input-item">暂无数据</div>
          </div>
        </div>
        
        <div class="info-section">
          <div class="section-label">支撑数据库</div>
          <div class="section-content database-content">
            <template v-if="selectedNode.supportDatabase && selectedNode.supportDatabase.length > 0">
              <div v-for="(item, index) in selectedNode.supportDatabase" :key="'db-' + index" class="database-item">
                {{ item }}
              </div>
            </template>
            <div v-else class="database-item">暂无数据</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
import * as ForceGraph3DModule from '3d-force-graph'
import { graphData } from '../utils/DecisionChain/DecisionChainData'
import * as THREE from 'three'
import blueImg from '@/assets/DecisionChain/texture-node-blue.png'
import orangeImg from '@/assets/DecisionChain/texture-node-orange.png'
import greenImg from '@/assets/DecisionChain/texture-node-green.png'

// 图形容器引用
const graphContainer = ref<HTMLElement | null>(null)
let graph: any = null
let animationFrameId: number | null = null

// 加载贴图
const textureLoader = new THREE.TextureLoader()
const blueTexture = textureLoader.load(blueImg)
// 设置贴图环绕模式,确保完全铺满
blueTexture.wrapS = THREE.RepeatWrapping
blueTexture.wrapT = THREE.RepeatWrapping // 垂直方向也使用重复环绕
// 提高贴图质量
blueTexture.anisotropy = 1024
// 设置贴图过滤模式
blueTexture.minFilter = THREE.LinearFilter
blueTexture.magFilter = THREE.LinearFilter

// 加载橙色贴图(三级节点)
const orangeTexture = textureLoader.load(orangeImg)
orangeTexture.wrapS = THREE.RepeatWrapping
orangeTexture.wrapT = THREE.RepeatWrapping
orangeTexture.anisotropy = 1024
orangeTexture.minFilter = THREE.LinearFilter
orangeTexture.magFilter = THREE.LinearFilter

// 加载绿色贴图(四级节点)
const greenTexture = textureLoader.load(greenImg)
greenTexture.wrapS = THREE.RepeatWrapping
greenTexture.wrapT = THREE.RepeatWrapping
greenTexture.anisotropy = 1024
greenTexture.minFilter = THREE.LinearFilter
greenTexture.magFilter = THREE.LinearFilter

// 控制参数
const linkStrength = 0.6//连接强度也就是连接线的长度
const linkDistance1_2 = 90 // 1级-2级连线长度
const linkDistance2_3 = 90 // 2级-3级连线长度
const linkDistance3_4 = 120  // 3级-4级连线长度
const selectedNode = ref<any>(null)
const hoveredNode = ref<any>(null) // 当前悬停的节点

// 根据节点层级获取颜色
const getNodeColor = (level: number): string => {
  switch (level) {
    case 1: return '#00A8FF' // 一级节点：蓝色
    case 2: return '#00A8FF' // 一级节点：蓝色
    case 3: return '#FF8C00' // 二级节点：橙色
    case 4: return '#00FF7F' // 三级节点：绿色
    default: return '#FFFFFF'
  }
}

// 根据节点层级获取节点半径
const getNodeRadius = (level: number): number => {
  switch (level) {
    case 1: return 60 // 一级节点半径
    case 2: return 35 // 二级节点半径
    case 3: return 24  // 三级节点半径
    default: return 14
  }
}

// 获取与指定节点直接相连的所有节点ID
const getConnectedNodeIds = (nodeId: number): Set<number> => {
  const connectedIds = new Set<number>()
  connectedIds.add(nodeId) // 包含自己
  
  graphData.links.forEach((link: any) => {
    const sourceId = typeof link.source === 'object' ? link.source.id : link.source
    const targetId = typeof link.target === 'object' ? link.target.id : link.target
    
    if (sourceId === nodeId) {
      connectedIds.add(targetId)
    }
    if (targetId === nodeId) {
      connectedIds.add(sourceId)
    }
  })
  
  return connectedIds
}

// 检查连线是否与悬停节点相关
const isLinkConnected = (link: any, nodeId: number): boolean => {
  const sourceId = typeof link.source === 'object' ? link.source.id : link.source
  const targetId = typeof link.target === 'object' ? link.target.id : link.target
  return sourceId === nodeId || targetId === nodeId
}

// 初始化3D力导向图
const initGraph = () => {
  console.log('[DecisionChain] 开始初始化3D力导向图')
  console.log('[DecisionChain] graphContainer.value:', graphContainer.value)
  
  if (!graphContainer.value) {
    console.error('[DecisionChain] graphContainer 未找到')
    return
  }

  // 检查容器尺寸
  const rect = graphContainer.value.getBoundingClientRect()
  console.log('[DecisionChain] 容器尺寸:', rect.width, 'x', rect.height)
  
  if (rect.width === 0 || rect.height === 0) {
    console.error('[DecisionChain] 容器尺寸为0，无法初始化')
    return
  }

  try {
    console.log('[DecisionChain] 创建 ForceGraph3D 实例')
    const ForceGraph3D = (ForceGraph3DModule as any).default || ForceGraph3DModule
    console.log('[DecisionChain] ForceGraph3D:', typeof ForceGraph3D)
    // 使用 new 关键字创建实例
    graph = new ForceGraph3D(graphContainer.value)
      .width(rect.width)
      .height(rect.height)
      .graphData(graphData)
      .nodeColor((node: any) => getNodeColor(node.level))
      .nodeRelSize(1)
      .nodeVal((node: any) => getNodeRadius(node.level))
      .nodeLabel(() => '') // 设置为空字符串，不显示悬停提示
      .nodeThreeObject((node: any) => {
        // 创建一个组来包含球体和文本
        const group = new THREE.Group()
        // 保存节点ID到userData,用于后续识别
        group.userData.nodeId = node.id
        
        const nodeRadius = getNodeRadius(node.level)
        
        // 创建文本精灵（先创建，确保在球体前面）
        const canvas = document.createElement('canvas')
        const context = canvas.getContext('2d')
        if (context) {
          canvas.width = 256
          canvas.height = 128
          
          // 一级节点使用更大的字体
          const fontSize = node.level === 1 ? 42 : 38
          context.font = `Bold ${fontSize}px Arial`
          context.textAlign = 'center'
          context.textBaseline = 'middle'
          
          // 根据节点层级设置不同的颜色和阴影
          if (node.level === 1) {
            context.shadowColor = '#99FFE5'
            context.shadowBlur = 2.4
            context.fillStyle = '#000000'
          } else if (node.level === 2) {
            context.shadowColor = '#00FFEE'
            context.shadowBlur = 3
            context.fillStyle = '#FFFFFF'
          } else if (node.level === 3) {
            context.shadowColor = '#FF5100'
            context.shadowBlur = 3
            context.fillStyle = '#FFFFFF'
          } else if (node.level === 4) {
            context.shadowColor = '#00FF84'
            context.shadowBlur = 3
            context.fillStyle = '#FFFFFF'
          } else {
            context.fillStyle = '#FFFFFF'
          }
          
          context.fillText(node.name, 128, 64)
          
          const texture = new THREE.CanvasTexture(canvas)
          // 一级节点禁用深度测试(文本在球体中心),其他节点启用深度测试(文本在球体上方)
          const spriteMaterial = new THREE.SpriteMaterial({ 
            map: texture,
            transparent: true,
            depthTest: node.level !== 1 // 一级节点禁用深度测试,其他节点启用
          })
          const sprite = new THREE.Sprite(spriteMaterial)
          sprite.renderOrder = 999 // 确保文本渲染在最前面
          
          // 根据节点层级设置文本位置和大小
          if (node.level === 1) {
            // 一级节点：文本在球体正中间
            sprite.scale.set(nodeRadius * 2.5, nodeRadius * 1.2, 1)
            sprite.position.set(0, 0, 0)
          } else {
            // 二、三、四级节点：文本在球体上方,需要动态更新位置
            sprite.scale.set(nodeRadius * 2, nodeRadius * 1, 1)
            // 保存节点信息用于动态更新位置
            sprite.userData.nodeRadius = nodeRadius
            sprite.userData.isAboveNode = true
          }
          
          group.add(sprite)
        }
        
        // 创建球体（后创建，在文本后面）
        const nodeColor = getNodeColor(node.level)
        // 增加球体分段数到64,提高贴图质量和消除缝隙
        const geometry = new THREE.SphereGeometry(nodeRadius, 64, 64)
        
        // 根据节点级别选择材质
        let material
        if (node.level === 1 || node.level === 2) {
          // 1、2级节点使用蓝色贴图
          material = new THREE.MeshBasicMaterial({
            map: blueTexture,
            transparent: true, // 启用透明度,支持暗化效果
            opacity: 1,
            side: THREE.FrontSide, // 只渲染正面
            depthWrite: true // 启用深度写入
          })
        } else if (node.level === 3) {
          // 3级节点使用橙色贴图
          material = new THREE.MeshBasicMaterial({
            map: orangeTexture,
            transparent: true,
            opacity: 1,
            side: THREE.FrontSide,
            depthWrite: true
          })
        } else if (node.level === 4) {
          // 4级节点使用绿色贴图
          material = new THREE.MeshBasicMaterial({
            map: greenTexture,
            transparent: true,
            opacity: 1,
            side: THREE.FrontSide,
            depthWrite: true
          })
        } else {
          // 其他节点使用纯色
          material = new THREE.MeshBasicMaterial({
            color: nodeColor,
            transparent: true,
            opacity: 1
          })
        }
        const sphere = new THREE.Mesh(geometry, material)
        group.add(sphere)
        
        // 添加外层光晕效果
        const glowGeometry = new THREE.SphereGeometry(nodeRadius * 1.4, 64, 64)
        let glowMaterial
        
        if (node.level === 1 || node.level === 2) {
          // 1、2级节点使用青色渐变光晕
          glowMaterial = new THREE.ShaderMaterial({
            transparent: true,
            side: THREE.BackSide,
            uniforms: {
              uOpacity: { value: 1.0 } // 添加透明度uniform
            },
            vertexShader: `
              varying vec3 vNormal;
              varying vec3 vPosition;
              void main() {
                vNormal = normalize(normalMatrix * normal);
                vPosition = position;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
              }
            `,
            fragmentShader: `
              uniform float uOpacity;
              varying vec3 vNormal;
              varying vec3 vPosition;
              void main() {
                // 归一化Y坐标到0-1范围
                float normalizedY = (normalize(vPosition).y + 1.0) / 2.0;
                
                // 使用smoothstep实现更平滑的过渡
                float gradientFactor = smoothstep(0.0, 1.0, normalizedY);
                
                // 顶部颜色: #5FFFF4 (rgb(95, 255, 244))
                vec3 topColor = vec3(95.0/255.0, 255.0/255.0, 244.0/255.0);
                float topAlpha = 1.0; // 不透明
                
                // 底部颜色: #000000 (黑色)
                vec3 bottomColor = vec3(0, 62, 91);
                float bottomAlpha = 0.2; // 完全透明
                
                // 使用平滑插值混合颜色和透明度
                vec3 color = mix(bottomColor, topColor, gradientFactor);
                float alpha = mix(bottomAlpha, topAlpha, gradientFactor);
                
                // 应用全局透明度控制
                alpha *= uOpacity;
                
                gl_FragColor = vec4(color, alpha);
              }
            `
          })
        } else if (node.level === 3) {
          // 3级节点使用橙色渐变光晕
          glowMaterial = new THREE.ShaderMaterial({
            transparent: true,
            side: THREE.BackSide,
            uniforms: {
              uOpacity: { value: 1.0 }
            },
            vertexShader: `
              varying vec3 vNormal;
              varying vec3 vPosition;
              void main() {
                vNormal = normalize(normalMatrix * normal);
                vPosition = position;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
              }
            `,
            fragmentShader: `
              uniform float uOpacity;
              varying vec3 vNormal;
              varying vec3 vPosition;
              void main() {
                float normalizedY = (normalize(vPosition).y + 1.0) / 2.0;
                float gradientFactor = smoothstep(0.0, 1.0, normalizedY);
                
                // 顶部颜色: #FFDF5F (rgb(255, 223, 95))
                vec3 topColor = vec3(255.0/255.0, 223.0/255.0, 95.0/255.0);
                float topAlpha = 1.0;
                
                // 底部颜色: #FF4000 (rgb(255, 64, 0))
                vec3 bottomColor = vec3(255.0/255.0, 64.0/255.0, 0.0/255.0);
                float bottomAlpha = 0.2;
                
                vec3 color = mix(bottomColor, topColor, gradientFactor);
                float alpha = mix(bottomAlpha, topAlpha, gradientFactor);
                alpha *= uOpacity;
                
                gl_FragColor = vec4(color, alpha);
              }
            `
          })
        } else if (node.level === 4) {
          // 4级节点使用绿色渐变光晕
          glowMaterial = new THREE.ShaderMaterial({
            transparent: true,
            side: THREE.BackSide,
            uniforms: {
              uOpacity: { value: 1.0 }
            },
            vertexShader: `
              varying vec3 vNormal;
              varying vec3 vPosition;
              void main() {
                vNormal = normalize(normalMatrix * normal);
                vPosition = position;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
              }
            `,
            fragmentShader: `
              uniform float uOpacity;
              varying vec3 vNormal;
              varying vec3 vPosition;
              void main() {
                float normalizedY = (normalize(vPosition).y + 1.0) / 2.0;
                float gradientFactor = smoothstep(0.0, 1.0, normalizedY);
                
                // 顶部颜色: #50FF7F (rgb(80, 255, 127))
                vec3 topColor = vec3(80.0/255.0, 255.0/255.0, 127.0/255.0);
                float topAlpha = 1.0;
                
                // 底部颜色: #98FF3E (rgb(152, 255, 62))
                vec3 bottomColor = vec3(152.0/255.0, 255.0/255.0, 62.0/255.0);
                float bottomAlpha = 0.2;
                
                vec3 color = mix(bottomColor, topColor, gradientFactor);
                float alpha = mix(bottomAlpha, topAlpha, gradientFactor);
                alpha *= uOpacity;
                
                gl_FragColor = vec4(color, alpha);
              }
            `
          })
        } else {
          // 其他节点使用纯色光晕
          glowMaterial = new THREE.MeshBasicMaterial({
            color: nodeColor,
            transparent: true,
            opacity: 0.2,
            side: THREE.BackSide
          })
        }
        
        const glowSphere = new THREE.Mesh(glowGeometry, glowMaterial)
        group.add(glowSphere)
        
        return group
      })
      .linkThreeObject((link: any) => {
        // 创建自定义连接线以实现渐变效果
        const sourceNode = graphData.nodes.find(n => n.id === link.source.id || n.id === link.source)
        const targetNode = graphData.nodes.find(n => n.id === link.target.id || n.id === link.target)
        
        if (!sourceNode || !targetNode) return null
        
        const sourceColor = new THREE.Color(getNodeColor(sourceNode.level))
        const targetColor = new THREE.Color(getNodeColor(targetNode.level))
        
        // 如果是四级节点之间的连线，使用红色
        if (sourceNode.level === 4 && targetNode.level === 4) {
          sourceColor.set('#FF0000')
          targetColor.set('#FF0000')
        }
        
        // 创建渐变线条的几何体
        const points = []
        const colors = []
        const segments = 20 // 分段数，越多渐变越平滑
        
        for (let i = 0; i <= segments; i++) {
          const t = i / segments
          points.push(new THREE.Vector3(0, 0, 0)) // 位置会在每帧更新
          // 颜色渐变
          const color = new THREE.Color().lerpColors(sourceColor, targetColor, t)
          colors.push(color.r, color.g, color.b)
        }
        
        const geometry = new THREE.BufferGeometry().setFromPoints(points)
        geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3))
        
        const material = new THREE.LineBasicMaterial({
          vertexColors: true,
          transparent: true,
          opacity: 0.6,
          linewidth: 4
        })
        
        const line = new THREE.Line(geometry, material)
        
        // 创建多个箭头用于动画
        const arrows: any[] = []
        const arrowCount = 1 // 每条线上1个箭头
        
        // 只有四级节点之间才显示箭头
        if (sourceNode.level === 4 && targetNode.level === 4) {
          for (let i = 0; i < arrowCount; i++) {
            // 调整箭头大小 (半径, 高度, 分段数)
            const arrowGeometry = new THREE.ConeGeometry(6, 10, 14)
            const arrowMaterial = new THREE.MeshBasicMaterial({
              color: targetColor,
              transparent: true,
              opacity: 0.8
            })
            const arrow = new THREE.Mesh(arrowGeometry, arrowMaterial)
            arrows.push(arrow)
          }
        }
        
        const group = new THREE.Group()
        group.add(line)
        arrows.forEach(arrow => group.add(arrow))
        
        // 保存引用以便后续更新位置
        group.userData.line = line
        group.userData.arrows = arrows
        group.userData.segments = segments
        group.userData.arrowProgress = arrows.map((_, i) => i / arrowCount) // 初始位置错开
        group.userData.sourceColor = sourceColor
        group.userData.targetColor = targetColor
        
        return group
      })
      .linkThreeObjectExtend(true) // 保留默认的位置更新
      .linkPositionUpdate((obj: any, { start, end }: any) => {
        // 更新线条和箭头的位置
        if (!obj.userData.line || !obj.userData.arrows) return
        
        const line = obj.userData.line
        const arrows = obj.userData.arrows
        const segments = obj.userData.segments
        const sourceColor = obj.userData.sourceColor
        const targetColor = obj.userData.targetColor
        
        // 更新线条各点位置
        const positions = line.geometry.attributes.position.array
        for (let i = 0; i <= segments; i++) {
          const t = i / segments
          positions[i * 3] = start.x + (end.x - start.x) * t
          positions[i * 3 + 1] = start.y + (end.y - start.y) * t
          positions[i * 3 + 2] = start.z + (end.z - start.z) * t
        }
        line.geometry.attributes.position.needsUpdate = true
        
        // 更新箭头动画
        const speed = 0.001 // 箭头移动速度
        arrows.forEach((arrow: any, index: number) => {
          // 更新进度
          obj.userData.arrowProgress[index] = (obj.userData.arrowProgress[index] + speed) % 1
          const t = obj.userData.arrowProgress[index]
          
          // 计算箭头位置
          const x = start.x + (end.x - start.x) * t
          const y = start.y + (end.y - start.y) * t
          const z = start.z + (end.z - start.z) * t
          arrow.position.set(x, y, z)
          
          // 计算箭头方向
          const direction = new THREE.Vector3(end.x - start.x, end.y - start.y, end.z - start.z).normalize()
          arrow.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction)
          
          // 根据位置设置箭头颜色（渐变）
          const arrowColor = new THREE.Color().lerpColors(sourceColor, targetColor, t)
          arrow.material.color = arrowColor
        })
      })
      .backgroundColor('rgba(0, 0, 0, 0)')
      .onNodeClick((node: any) => {
        selectedNode.value = node
        console.log('[DecisionChain] 选中节点:', node)
      })
      .onNodeHover((node: any) => {
        // 更新悬停状态
        hoveredNode.value = node
        
        // 更新鼠标样式
        if (graphContainer.value) {
          graphContainer.value.style.cursor = node ? 'pointer' : 'default'
        }
        
        // 应用高亮效果
        const scene = graph.scene()
        if (!scene) return
        
        if (node) {
          const connectedIds = getConnectedNodeIds(node.id)
          
          // 遍历场景中的所有对象
          scene.traverse((obj: any) => {
            // 处理节点对象 - 通过userData.nodeId识别
            if (obj.userData && obj.userData.nodeId !== undefined) {
              const nodeId = obj.userData.nodeId
              const isConnected = connectedIds.has(nodeId)
              
              // 遍历节点组中的所有子对象
              obj.traverse((child: any) => {
                if (child.material) {
                  // 判断是否是ShaderMaterial
                  const isShaderMaterial = child.material.type === 'ShaderMaterial'
                  
                  if (isShaderMaterial && child.material.uniforms?.uOpacity) {
                    // ShaderMaterial: 使用uniform控制透明度
                    if (!child.material.userData) child.material.userData = {}
                    if (child.material.userData.originalOpacity === undefined) {
                      child.material.userData.originalOpacity = child.material.uniforms.uOpacity.value
                    }
                    
                    if (isConnected) {
                      child.material.uniforms.uOpacity.value = child.material.userData.originalOpacity
                    } else {
                      child.material.uniforms.uOpacity.value = 0.05
                    }
                  } else {
                    // 普通材质: 使用opacity属性
                    if (!child.material.userData) child.material.userData = {}
                    if (child.material.userData.originalOpacity === undefined) {
                      child.material.userData.originalOpacity = child.material.opacity
                    }
                    
                    if (isConnected) {
                      child.material.opacity = child.material.userData.originalOpacity
                    } else {
                      child.material.opacity = 0.05
                    }
                  }
                }
              })
            }
            
            // 处理连线对象
            if (obj.__graphObjType === 'link' && obj.__data) {
              const linkData = obj.__data
              const isConnected = isLinkConnected(linkData, node.id)
              
              // 遍历连线组中的所有子对象
              obj.traverse((child: any) => {
                if (child.material) {
                  // 保存原始透明度(首次悬停时)
                  if (!child.material.userData) child.material.userData = {}
                  if (child.material.userData.originalOpacity === undefined) {
                    child.material.userData.originalOpacity = child.material.opacity
                  }
                  
                  if (isConnected) {
                    // 相关连线保持正常
                    child.material.opacity = child.material.userData.originalOpacity
                  } else {
                    // 不相关连线添加暗化效果
                    child.material.opacity = 0.05
                    child.material.transparent = true
                  }
                }
              })
            }
          })
        } else {
          // 鼠标移开,恢复所有节点和连线
          scene.traverse((obj: any) => {
            // 恢复节点
            if (obj.userData && obj.userData.nodeId !== undefined) {
              obj.traverse((child: any) => {
                if (child.material && child.material.userData?.originalOpacity !== undefined) {
                  // 判断是否是ShaderMaterial
                  const isShaderMaterial = child.material.type === 'ShaderMaterial'
                  
                  if (isShaderMaterial && child.material.uniforms?.uOpacity) {
                    child.material.uniforms.uOpacity.value = child.material.userData.originalOpacity
                  } else {
                    child.material.opacity = child.material.userData.originalOpacity
                  }
                }
              })
            }
            // 恢复连线
            if (obj.__graphObjType === 'link' && obj.__data) {
              obj.traverse((child: any) => {
                if (child.material && child.material.userData?.originalOpacity !== undefined) {
                  child.material.opacity = child.material.userData.originalOpacity
                }
              })
            }
          })
        }
      })

    // 设置连接强度
    const linkForce = graph.d3Force('link')
    if (linkForce) {
      linkForce.strength(linkStrength)
      // 设置连线距离
      linkForce.distance((link: any) => {
        const source = link.source
        const target = link.target
        const l1 = source.level
        const l2 = target.level
        const minL = Math.min(l1, l2)
        const maxL = Math.max(l1, l2)
        
        if (minL === 1 && maxL === 2) return linkDistance1_2
        if (minL === 2 && maxL === 3) return linkDistance2_3
        if (minL === 3 && maxL === 4) return linkDistance3_4
        
        return 50 // 默认距离
      })
    }

    // 设置电荷力（排斥力）
    const chargeForce = graph.d3Force('charge')
    if (chargeForce) {
      chargeForce.strength((node: any) => {
        // 一级节点给予极强的排斥力，使其在空间上充分分散
        if (node.level === 1) return -200
        // 二级节点适中
        if (node.level === 2) return -500
        // 其他节点
        return -150
      })
    }

    // 设置相机位置（离得更远以便看到完整图形）
    graph.cameraPosition({ x: 0, y: 0, z: 1500 }, { x: 0, y: 0, z: 0 })
    
    // 启动箭头动画
    startArrowAnimation()
    
    // 使用 setTimeout 确保 DOM 已渲染后再移除提示信息和调整容器尺寸
    setTimeout(() => {
      // 移除提示信息
      const infoElements = graphContainer.value?.querySelectorAll('.graph-info-msg, .scene-nav-info')
      if (infoElements && infoElements.length > 0) {
        infoElements.forEach(el => el.remove())
        console.log('[DecisionChain] 已移除控制提示信息')
      }
      
      // 强制设置 scene-container 尺寸为 100%
      const sceneContainer = graphContainer.value?.querySelector('.scene-container') as HTMLElement
      if (sceneContainer) {
        sceneContainer.style.width = '100%'
        sceneContainer.style.height = '100%'
        console.log('[DecisionChain] 已设置 scene-container 尺寸为 100%')
      }
    }, 100)
    
    console.log('[DecisionChain] 3D力导向图初始化成功')
    console.log('[DecisionChain] 画布尺寸:', rect.width, 'x', rect.height)
  } catch (error) {
    console.error('[DecisionChain] 初始化3D力导向图失败:', error)
  }
}

// 箭头动画函数
const startArrowAnimation = () => {
  const animate = () => {
    if (!graph) return
    
    // 获取相机
    const camera = graph.camera()
    
    // 获取所有连接线对象
    const scene = graph.scene()
    if (scene) {
      scene.traverse((obj: any) => {
        // 更新二、三、四级节点的文字位置,使其始终在球体正上方(相对于相机视角)
        if (obj.userData && obj.userData.isAboveNode) {
          const nodeRadius = obj.userData.nodeRadius
          
          // 获取节点组的世界位置
          const nodeWorldPos = new THREE.Vector3()
          obj.parent.getWorldPosition(nodeWorldPos)
          
          // 获取相机位置
          const cameraPos = new THREE.Vector3()
          camera.getWorldPosition(cameraPos)
          
          // 计算从节点到相机的方向
          const dirToCamera = new THREE.Vector3().subVectors(cameraPos, nodeWorldPos).normalize()
          
          // 使用相机的上方向作为参考
          const cameraUp = camera.up.clone().normalize()
          
          // 计算垂直于视线的右方向
          const right = new THREE.Vector3().crossVectors(cameraUp, dirToCamera).normalize()
          
          // 重新计算真正的上方向(垂直于视线和右方向)
          const up = new THREE.Vector3().crossVectors(dirToCamera, right).normalize()
          
          // 将文本放置在球体上方(相对于相机视角)
          const offset = nodeRadius * 1.8
          const textPos = up.multiplyScalar(offset)
          obj.position.copy(textPos)
        }
        
        // 查找包含箭头的组
        if (obj.userData && obj.userData.arrows && obj.userData.arrowProgress) {
          const arrows = obj.userData.arrows
          const sourceColor = obj.userData.sourceColor
          const targetColor = obj.userData.targetColor
          const speed = 0.005
          
          // 获取线条的起点和终点
          const line = obj.userData.line
          if (line && line.geometry && line.geometry.attributes.position) {
            const positions = line.geometry.attributes.position.array
            const segments = obj.userData.segments
            
            arrows.forEach((arrow: any, index: number) => {
              // 更新进度
              obj.userData.arrowProgress[index] = (obj.userData.arrowProgress[index] + speed) % 1
              const t = obj.userData.arrowProgress[index]
              
              // 计算箭头在线条上的位置
              const segmentIndex = Math.floor(t * segments)
              const segmentT = (t * segments) - segmentIndex
              
              if (segmentIndex < segments) {
                const i1 = segmentIndex * 3
                const i2 = (segmentIndex + 1) * 3
                
                const x = positions[i1] + (positions[i2] - positions[i1]) * segmentT
                const y = positions[i1 + 1] + (positions[i2 + 1] - positions[i1 + 1]) * segmentT
                const z = positions[i1 + 2] + (positions[i2 + 2] - positions[i1 + 2]) * segmentT
                
                arrow.position.set(x, y, z)
                
                // 计算方向
                const dx = positions[i2] - positions[i1]
                const dy = positions[i2 + 1] - positions[i1 + 1]
                const dz = positions[i2 + 2] - positions[i1 + 2]
                const direction = new THREE.Vector3(dx, dy, dz).normalize()
                arrow.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction)
                
                // 根据位置设置箭头颜色（渐变）
                const arrowColor = new THREE.Color().lerpColors(sourceColor, targetColor, t)
                arrow.material.color = arrowColor
              }
            })
          }
        }
      })
    }
    
    animationFrameId = requestAnimationFrame(animate)
  }
  
  animate()
}

// 更新图形参数（已移除，连接强度固定为 0.1）

// 重置相机视角
const resetCamera = () => {
  if (!graph) return
  graph.cameraPosition(
    { x: 0, y: 0, z: 600 },
    { x: 0, y: 0, z: 0 },
    1000
  )
}

// 处理窗口大小变化
const handleResize = () => {
  if (!graph || !graphContainer.value) return
  
  const rect = graphContainer.value.getBoundingClientRect()
  if (rect.width > 0 && rect.height > 0) {
    graph.width(rect.width).height(rect.height)
    console.log('[DecisionChain] 窗口大小已调整:', rect.width, 'x', rect.height)
  }
}

// 组件挂载
onMounted(() => {
  console.log('[DecisionChain] 组件已挂载')
  // 使用 nextTick 确保 DOM 完全渲染后再初始化
  nextTick(() => {
    console.log('[DecisionChain] nextTick 执行，开始初始化')
    // 添加短暂延迟确保容器尺寸已计算
    setTimeout(() => {
      initGraph()
    }, 100)
  })
  
  // 监听窗口大小变化
  window.addEventListener('resize', handleResize)
})

// 组件卸载
onUnmounted(() => {
  console.log('[DecisionChain] 组件卸载，移除窗口监听')
  window.removeEventListener('resize', handleResize)
  
  // 停止动画
  if (animationFrameId !== null) {
    cancelAnimationFrame(animationFrameId)
    animationFrameId = null
  }
  
  if (graph) {
    graph._destructor()
  }
})
</script>

<style scoped>
.decision-chain-container {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
}

.graph-container {
  width: 100%;
  height: 100%;
}

/* 隐藏 3d-force-graph 默认的控制提示信息 */
.graph-container :deep(.graph-info-msg),
.graph-container :deep(.scene-nav-info) {
  display: none !important;
}

/* 确保 scene-container 和 canvas 元素与父容器尺寸一致 */
.graph-container :deep(.scene-container) {
  width: 100% !important;
  height: 100% !important;
  position: relative !important;
}

.graph-container :deep(canvas) {
  width: 100% !important;
  height: 100% !important;
  display: block;
}

.control-panel {
  position: absolute;
  bottom: 30px;
  left: 30px;
  background: rgba(0, 0, 0, 0.7);
  border: 1px solid rgba(212, 255, 239, 0.3);
  border-radius: 8px;
  padding: 20px;
  backdrop-filter: blur(10px);
  z-index: 10;
  min-width: 280px;
}

.control-item {
  display: flex;
  align-items: center;
  gap: 15px;
  margin-bottom: 15px;
}

.control-item:last-of-type {
  margin-bottom: 0;
}

.control-item label {
  font-family: 'Source Han Sans', sans-serif;
  font-size: 14px;
  color: #D4FFEF;
  min-width: 70px;
}

.control-item input[type="range"] {
  flex: 1;
  height: 4px;
  background: rgba(212, 255, 239, 0.2);
  border-radius: 2px;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}

.control-item input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  background: #06FFB0;
  border-radius: 50%;
  cursor: pointer;
  box-shadow: 0 0 5px rgba(6, 255, 176, 0.5);
}

.control-item input[type="range"]::-moz-range-thumb {
  width: 14px;
  height: 14px;
  background: #06FFB0;
  border-radius: 50%;
  cursor: pointer;
  border: none;
  box-shadow: 0 0 5px rgba(6, 255, 176, 0.5);
}

.control-item span {
  font-family: 'Source Han Sans', sans-serif;
  font-size: 14px;
  color: #06FFB0;
  min-width: 30px;
  text-align: right;
}

.reset-btn {
  width: 100%;
  height: 36px;
  background: rgba(6, 255, 176, 0.1);
  border: 1px solid #06FFB0;
  border-radius: 4px;
  color: #06FFB0;
  font-family: 'Source Han Sans', sans-serif;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.3s;
  margin-top: 10px;
}

.reset-btn:hover {
  background: rgba(6, 255, 176, 0.2);
  box-shadow: 0 0 10px rgba(6, 255, 176, 0.3);
}

.info-panel {
  position: absolute;
  top: 120px;
  right: 30px;
  width: 299px;
  background: #101519;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 2px;
  box-sizing: border-box;
  z-index: 10;
  overflow: hidden;
}

.info-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 15px;
  background: linear-gradient(180deg, rgba(0, 205, 154, 0.8) 0%, rgba(0, 205, 154, 0) 100%);
  box-sizing: border-box;
  border-width: 0px 0px 1px 0px;
  border-style: solid;
  border-color: rgba(255, 255, 255, 0.3);
  font-weight: bold;
  font-size: 14px;
}

.info-header h3 {
  font-family: 'Source Han Sans', sans-serif;
  font-size: 16px;
  font-weight: 600;
  color: #D4FFEF;
  margin: 0;
}

.close-btn {
  width: 24px;
  height: 24px;
  background: transparent;
  border: none;
  color: #D4FFEF;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  transition: all 0.3s;
  padding: 0;
}

.close-btn:hover {
  color: #06FFB0;
  transform: rotate(90deg);
}

.info-content {
  padding: 15px;
}

.info-section {
  margin-bottom: 15px;
}

.info-section:last-child {
  margin-bottom: 0;
}

.section-label {
  font-family: 'Source Han Sans', sans-serif;
  font-size: 14px;
  color: #D4FFEF;
  margin-bottom: 8px;
}

.section-content {
  border-radius: 3px;
  padding: 8px 7px;
  background: rgba(45, 103, 91, 0.2);
  box-sizing: border-box;
  border: 1px solid rgba(212, 255, 239, 0.3);
}

.analysis-content .content-item {
  font-family: 'Source Han Sans', sans-serif;
  font-size: 14px;
  color: #D4FFEF;
  margin-bottom: 5px;
  line-height: 1.5;
}

.analysis-content .content-item:last-child {
  margin-bottom: 0;
}

.input-content .input-item,
.database-content .database-item {
  font-family: 'Source Han Sans', sans-serif;
  font-size: 14px;
  color: #D4FFEF;
  padding: 6px 8px;
  margin-bottom: 5px;
  border-radius: 3px;
  border: 1px solid rgba(212, 255, 239, 0.3);
  background: rgba(0, 0, 0, 0.2);
}

.input-content .input-item:last-child,
.database-content .database-item:last-child {
  margin-bottom: 0;
}
</style>
